﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Linq;

namespace Gmantis.Controls.DataGrid
{
    internal enum RecyclingModes
    {
        Disabled,
        KeepRemovedChildren,
        RemoveChildren
    }

    internal interface IRecyclable
    {
        // Methods
        void Collect();
        object GetRecyclingKey();
        void Reuse();

        // Properties
        bool IsCollected { get; }
    }

    public abstract class RecyclingPanel : Panel
    {
        // Fields
        internal bool HideClipping = true;
        internal RecyclingModes Mode = RecyclingModes.KeepRemovedChildren;
        internal Dictionary<object, Stack<UIElement>> recyclingQueues = new Dictionary<object, Stack<UIElement>>();

        // Events
        internal event EventHandler ChildrenChanged;

        // Methods
        protected RecyclingPanel()
        {
        }

        internal virtual void Clear()
        {
            foreach (UIElement element in new List<UIElement>(base.Children.GetTypedUIElementCollectionWrapper()))
            {
                IRecyclable recyclable = element as IRecyclable;
                if ((recyclable != null) && !recyclable.IsCollected)
                {
                    recyclable.Collect();
                }
            }
            base.Children.Clear();
            foreach (KeyValuePair<object, Stack<UIElement>> pair in this.recyclingQueues)
            {
                pair.Value.Clear();
            }
            this.recyclingQueues.Clear();
            this.OnChildrenChanged();
        }

        protected void ClipPresenterHorizontally(UIElement presenter, double presenterRenderWidth, double presenterRenderHeight, double topOffset, double bottomOffset, double offset)
        {
            double y = 0.0;
            double height = presenterRenderHeight;
            if (offset < topOffset)
            {
                y = topOffset - offset;
            }
            if ((offset + presenterRenderHeight) > bottomOffset)
            {
                height = Math.Max((double)0.0, (double)((bottomOffset - offset) - y));
            }
            if ((y > 0.0) || (height != presenterRenderHeight))
            {
                RectangleGeometry geometry = new RectangleGeometry
                {
                    Rect = new Rect(0.0, y, presenterRenderWidth, height)
                };
                presenter.Clip = geometry;
            }
            else
            {
                presenter.Clip = null;
            }
        }

        protected void ClipPresenterVertically(UIElement presenter, double presenterRenderWidth, double presenterRenderHeight, double leftOffset, double rightOffset, double offset)
        {
            double x = 0.0;
            double width = presenterRenderWidth;
            if (offset < leftOffset)
            {
                x = leftOffset - offset;
            }
            if ((offset + presenterRenderWidth) > rightOffset)
            {
                width = Math.Max((double)0.0, (double)((rightOffset - offset) - x));
            }
            if ((x > 0.0) || (width != presenterRenderWidth))
            {
                RectangleGeometry geometry = new RectangleGeometry
                {
                    Rect = new Rect(x, 0.0, width, presenterRenderHeight)
                };
                presenter.Clip = geometry;
            }
            else
            {
                presenter.Clip = null;
            }
        }

        internal virtual UIElement GetChild(object key, Func<UIElement> creator)
        {
            UIElement child = null;
            if (this.Mode != RecyclingModes.Disabled)
            {
                Stack<UIElement> recyclingCollection = this.GetRecyclingCollection(key);
                if ((recyclingCollection != null) && (recyclingCollection.Count > 0))
                {
                    child = recyclingCollection.Pop();
                    IRecyclable recyclable = (IRecyclable)child;
                    this.Show(child);
                    if (this.Mode == RecyclingModes.RemoveChildren)
                    {
                        base.Children.Add(child);
                    }
                    recyclable.Reuse();
                }
            }
            if (child == null)
            {
                child = creator();
                base.Children.Add(child);
            }
            this.OnChildrenChanged();
            return child;
        }

        private Stack<UIElement> GetRecyclingCollection(object key)
        {
            Stack<UIElement> stack = null;
            if ((key != null) && !this.recyclingQueues.TryGetValue(key, out stack))
            {
                stack = new Stack<UIElement>();
                this.recyclingQueues[key] = stack;
            }
            return stack;
        }

        internal void Hide(UIElement child)
        {
            if (this.HideClipping)
            {
                child.Clip = new RectangleGeometry();
            }
            else
            {
                child.Visibility = Visibility.Collapsed;
            }
        }

        private void OnChildrenChanged()
        {
            if (this.ChildrenChanged != null)
            {
                this.ChildrenChanged(this, new EventArgs());
            }
        }

        internal virtual void RemoveChild(UIElement child)
        {
            IRecyclable recyclable = (IRecyclable)child;
            if (!recyclable.IsCollected)
            {
                bool flag = false;
                if (this.Mode != RecyclingModes.Disabled)
                {
                    Stack<UIElement> recyclingCollection = this.GetRecyclingCollection(recyclable.GetRecyclingKey());
                    if (recyclingCollection != null)
                    {
                        recyclingCollection.Push(child);
                        if (this.Mode == RecyclingModes.KeepRemovedChildren)
                        {
                            this.Hide(child);
                            flag = true;
                        }
                    }
                }
                recyclable.Collect();
                if (!flag)
                {
                    base.Children.Remove(child);
                }
                this.OnChildrenChanged();
            }
        }

        internal void RemoveChildren()
        {
            UIElement[] array = new UIElement[base.Children.Count];
            base.Children.CopyTo(array, 0);
            foreach (UIElement element in array)
            {
                IRecyclable recyclable = element as IRecyclable;
                if ((recyclable != null) && !recyclable.IsCollected)
                {
                    this.RemoveChild(element);
                }
            }
        }

        internal void Show(UIElement child)
        {
            if (this.HideClipping)
            {
                child.Clip = null;
            }
            else
            {
                child.Visibility = Visibility.Visible;
            }
        }

        public override string ToString()
        {
            return string.Format("Children {0}, Keys [{1}]", base.Children.Count, string.Join(";", (from pair in this.recyclingQueues select string.Format("{0},{1}", pair.Key, pair.Value.Count)).ToArray<string>()));
        }
    }
}
